package client.tools;

/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 * ImagesLoader.java
 *
 * Steven Louie Apr. 8, 2012
 *
 * This class is in charge of loading all the images necessary for the program.
 * It reads an image configuration file which describes which images will be
 * read into the program. They are then loaded into a HashMap of BufferedImages
 * when requested.
 */
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;

public class ImagesLoader {

    private static final String IMAGE_DIR = "res/images/";
    private static final String CONF_NAME = "imsInfo.conf";
    private HashMap<String, ArrayList<Image>> imagesMap;
    private GraphicsConfiguration gc;
    private static ImagesLoader instance;

    private ImagesLoader() {
        imagesMap = new HashMap<>();
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
        instance = this;
        loadImagesFile(CONF_NAME);
    }

    public static ImagesLoader getInstance() {
        if (instance == null) {
            instance = new ImagesLoader();
        }
        return instance;
    }

    /**
     * Process the image configuration file
     *
     * @param fnm Image configuration file name
     */
    private void loadImagesFile(String fnm) {
        String imsFNm = IMAGE_DIR + fnm;
        System.out.println("Reading file: " + imsFNm);
        try {
            String respath = System.getProperty("res.path");
            if (respath == null) {
                respath = "";
            }
            InputStream in = new FileInputStream(respath + IMAGE_DIR + CONF_NAME);
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            String line;
            char ch;
            while ((line = br.readLine()) != null) {
                if (line.length() == 0) {
                    continue;
                }
                if (line.startsWith("#")) {
                    continue;
                }
                ch = Character.toLowerCase(line.charAt(0));
                if (ch == 'o') {
                    getFileNameImage(line);
                } else if (ch == 'n') {
                    getNumberedImages(line);
                } else {
                    System.out.println("Do not recognize line: " + line);
                }
            }
            br.close();
        } catch (IOException e) {
            System.out.println("Error reading file: " + imsFNm);
            System.exit(1);
        }
    }

    // =========================== Load single image ===========================
    /**
     * Tokenize and load type o image
     *
     * @param line The loaded line from the configuration file
     */
    private void getFileNameImage(String line) {
        StringTokenizer tokens = new StringTokenizer(line);
        if (tokens.countTokens() != 2) {
            System.out.println("Wrong no. of args for " + line);
        } else {
            tokens.nextToken();
            loadSingleImage(tokens.nextToken());
        }
    }

    /**
     * Load a single image and add to image map
     *
     * @param fnm The image file name
     */
    public boolean loadSingleImage(String fnm) {
        String name = getPrefix(fnm);
        if (imagesMap.containsKey(name)) {
            System.out.println("Error: " + name + "already used");
            return false;
        }
        Image bi = loadImage(fnm);
        if (bi != null) {
            ArrayList<Image> imsList = new ArrayList<>();
            imsList.add(bi);
            imagesMap.put(name, imsList);
            //System.out.println("Stored " + fnm);
            return true;
        } else {
            return false;
        }
    }

    /**
     * Extract the prefix of a filename
     *
     * @param fnm File name including extension
     * @return The prefix
     */
    private String getPrefix(String fnm) {
        int startposn;
        int endposn;

        startposn = fnm.lastIndexOf("/");

        if ((endposn = fnm.lastIndexOf(".")) == -1) {
            System.out.println("No prefix found for filename: " + fnm);
            return fnm;
        } else {
            return fnm.substring(startposn + 1, endposn);
        }
    }

    // ========================== Load numbered
    // images===========================
    /**
     * Tokenize and load type n image
     *
     * @param line The loaded line from the configuration file
     */
    private void getNumberedImages(String line) {
        StringTokenizer tokens = new StringTokenizer(line);

        if (tokens.countTokens() != 3) {
            System.out.println("Wrong no. of args for " + line);
        } else {
            tokens.nextToken();

            String fnm = tokens.nextToken();
            int number = -1;
            try {
                number = Integer.parseInt(tokens.nextToken());
            } catch (Exception e) {
                System.out.println("Number is incorrect for " + line);
            }
            loadNumImages(fnm, number);
        }
    }

    /**
     * Parse and load type n images
     *
     * @param fnm File name
     * @param number Number of image files
     * @return The number of loaded images
     */
    public int loadNumImages(String fnm, int number) {
        String dir = null;
        String prefix = null;
        String postfix = null;
        int dirPosn;
        dirPosn = fnm.lastIndexOf("/");
        int starPosn = fnm.lastIndexOf("*");
        if (starPosn == -1) {
            System.out.println("No '*' in filename: " + fnm);
            prefix = getPrefix(fnm);
        } else {
            dir = fnm.substring(0, dirPosn + 1);
            prefix = fnm.substring(dirPosn + 1, starPosn);
            postfix = fnm.substring(starPosn + 1);
        }
        if (imagesMap.containsKey(prefix)) {
            System.out.println("Error: " + prefix + "already used");
            return 0;
        }

        return loadNumImages(dir, prefix, postfix, number);
    }

    /**
     * Read in the images and put into image map
     *
     * @param dir The directory of the files
     * @param prefix The prefix of the filename before the
     *
     * @param postfix The file extension
     * @param number The number of files with the prefix
     * @return The number of images loaded
     */
    private int loadNumImages(String dir, String prefix, String postfix,
            int number) {
        String imFnm;
        Image bi;
        ArrayList<Image> imsList = new ArrayList<>();
        int loadCount = 0;
        if (number <= 0) {
            System.out.println("Error: Number <= 0: " + number);
            imFnm = dir + prefix + postfix;
            if ((bi = loadImage(imFnm)) != null) {
                loadCount++;
                imsList.add(bi);
                System.out.println("Stored " + prefix + "/" + imFnm);
            }
        } else {
            for (int i = 0; i < number; i++) {
                imFnm = dir + prefix + i + postfix;
                if ((bi = loadImage(imFnm)) != null) {
                    loadCount++;
                    imsList.add(bi);
                }
            }
        }

        if (loadCount == 0) {
            System.out.println("No images loaded for " + prefix);
        } else {
            imagesMap.put(prefix, imsList);
        }

        return loadCount;
    }

    // ============================================================================
    /**
     * Read an image
     *
     * @param fnm File name
     * @return The image as a BufferedImage
     */
    public Image loadImage(String fnm) {
        String respath = "";
        try {
            respath = System.getProperty("res.path");
            if (respath == null) {
                respath = "";
            }
            InputStream stream = new FileInputStream(respath + IMAGE_DIR + fnm);
            Image im = new Image(stream, fnm, false);
            System.out.println("Loading image: " + respath + IMAGE_DIR + fnm);
            return im;
        } catch (IOException | SlickException e) {
            System.out.println("Load Image error for " + respath + IMAGE_DIR + fnm
                    + ":\n" + e);
        }
        return null;
    }

    // =========== Access methods for the image map =================
    public Image getImage(String name) {
        ArrayList<Image> imsList = imagesMap.get(name);
        if (imsList == null) {
            System.out.println("No image(s) stored under " + name);
            return null;
        }
        return imsList.get(0);
    }

    public Image getImage(String name, int posn) {
        ArrayList<Image> imsList = imagesMap.get(name);
        if (imsList == null) {
            System.out.println("No image(s) stored under " + name);
            return null;
        }
        int size = imsList.size();
        if (posn < 0) {
            return imsList.get(0);
        } else if (posn >= size) {
            int newPosn = posn % size;
            return imsList.get(newPosn);
        }
        return imsList.get(posn);
    }

    public ArrayList<Image> getImages(String name) {
        ArrayList<Image> imsList = imagesMap.get(name);
        if (imsList == null) {
            System.out.println("No image(s) stored under " + name);
            return null;
        }
        return imsList;
    }

    public boolean isLoaded(String name) {
        ArrayList<Image> imsList = imagesMap.get(name);
        if (imsList == null) {
            return false;
        }
        return true;
    }

    public int numImages(String name) {
        ArrayList<Image> imsList = imagesMap.get(name);
        if (imsList == null) {
            System.out.println("No image(s) stored under " + name);
            return -1;
        }
        return imsList.size();
    }
}
